//
// Created by cjl on 2019-01-14.
//
#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/log.h>

#define LOGE(...) av_log(NULL, AV_LOG_ERROR,__VA_ARGS__)
#define LOGI(...) av_log(NULL, AV_LOG_INFO,__VA_ARGS__)

int main() {
    const char *srcPath = "/Users/caijinglong/code/c/demos/ffmpeg/log/src.mp4";
    const char *dstPath = "/Users/caijinglong/code/c/demos/ffmpeg/log/target.mp4";

    AVFormatContext *in_ctx = NULL;
    AVFormatContext *out_ctx = NULL;
    AVOutputFormat *out_format = NULL;
    AVPacket pkt;

    size_t mapping_size;
    int *stream_mapping = NULL;
    int ret;

    int main_ret = 0;

    int stream_index = 0;

    av_register_all();
    av_log_set_level(AV_LOG_INFO);
    if ((ret = avformat_open_input(&in_ctx, srcPath, 0, 0)) < 0) {
        LOGE("error info: %s", av_err2str(ret));
        main_ret = 1;
        goto end;
    }

    if ((ret = avformat_find_stream_info(in_ctx, 0)) < 0) {
        LOGE("Failed to retrieve input stream information: %s", av_err2str(ret));
        main_ret = 1;
        goto end;
    }

    ret = avformat_alloc_output_context2(&out_ctx, NULL, NULL, dstPath);
    if (ret < 0) {
        LOGE("cannot open target %s\n", av_err2str(ret));
        main_ret = 1;
        goto end;
    }


    if (ret < 0) {
        LOGE("error info: %s", av_err2str(ret));
        main_ret = 1;
        goto end;
    }

    av_dump_format(in_ctx, 0, srcPath, 0);

    mapping_size = in_ctx->nb_streams;

    stream_mapping = av_malloc_array(mapping_size, sizeof(*stream_mapping));

    if (!stream_mapping) {
        LOGE("error info: 不能申请内存\n");
        main_ret = 1;
        goto end;
    }

    out_format = out_ctx->oformat;

    LOGI("mapping size = %d\n", (int) mapping_size);

    for (int i = 0; i < mapping_size; ++i) {
        AVStream *in_stream = in_ctx->streams[i];
        AVStream *out_stream = NULL;

        AVCodecParameters *in_params = in_stream->codecpar;
        enum AVMediaType type = in_params->codec_type;
        LOGI("type = %d", type);
        if (type != AVMEDIA_TYPE_AUDIO &&
            type != AVMEDIA_TYPE_VIDEO &&
            type != AVMEDIA_TYPE_SUBTITLE
                ) {
            stream_mapping[i] = -1;
            continue;
        }

        LOGI("stream loop i = %d , type = %d\n", i, type);

        stream_mapping[i] = stream_index;
        stream_index++;

        out_stream = avformat_new_stream(out_ctx, NULL);

        if (out_stream == NULL) {
            LOGE("cannot alloc out stream\n");
            main_ret = 1;
            goto end;
        }

        ret = avcodec_parameters_copy(out_stream->codecpar, in_params);

        if (ret < 0) {
            LOGE("cannot copy codec params, info is %s\n", av_err2str(ret));
            main_ret = 1;
            goto end;
        }

        out_stream->codecpar->codec_tag = 0;
    }

    av_dump_format(out_ctx, 0, dstPath, 1);

    // 先创建文件
//    if (!(out_format->flags & AVFMT_NOFILE)) {
    ret = avio_open(&(out_ctx->pb), dstPath, AVIO_FLAG_READ_WRITE);
    if (ret < 0) {
        LOGE("cannot create dst file %s\n", av_err2str(ret));
        main_ret = 1;
        goto end;
    }
//    }

    ret = avformat_write_header(out_ctx, NULL);

    if (ret < 0) {
        LOGE("cannot write header, info is %s\n", av_err2str(ret));
        main_ret = 1;
        goto end;
    }

    // write frame

    while (1) {
        AVStream *in_stream, out_stream;

        ret = av_read_frame(in_ctx, &pkt);

        if (ret == 0) {
            break;
        }

        int st_index = pkt.stream_index;
        in_stream = in_ctx->streams[st_index];

        if (st_index >= mapping_size || stream_mapping[st_index] < 0) {
            av_packet_unref(&pkt);
            continue;
        }

        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream.time_base,
                                   AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream.time_base,
                                   AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream.time_base);
        pkt.pos = -1;

        ret = av_write_frame(out_ctx, &pkt);

        if (ret < 0) {
            LOGE("write frame error, info = %s\n", av_err2str(ret));
            break;
        }

        av_packet_unref(&pkt);
    }

    ret = av_write_trailer(out_ctx);
    if (ret < 0) {
        LOGE("cannot write trailer , info = %s\n", av_err2str(ret));
        main_ret = 1;
        goto end;
    }

    end:

    if (in_ctx) {
        avformat_close_input(&in_ctx);
    }

    if (out_ctx) {
        avformat_close_input(&out_ctx);
    }

    if (out_format) {
        avformat_free_context(out_format);
    }

    av_freep(stream_mapping);

    return main_ret;
}